Python supports many different kinds of data
Each is an instance of an object, and every object has:
Each instanceis a particular type of object
Everything in Python is an object and has a type
Objects are a data abstraction that capture:
Can create new instances of objects
Can destroy objects
Bundle data into packages together with procedures that work on them through well-defined interfaces
Divide-and-conquer development
Classes make it easy to reuse code
Use the classkeyword to define a new type
class Coordinate(object): <br>
- define attributes here
Similar to def, indent code to indicate which statements are part of the class definition
The word object means that Coordinateis a Python object and inherits all its attributes
Data and procedures that “belong” to the class
data attributes
procedural attributes (methods)
class Coordinate(object):
def __init__(self, x, y): # a special method called __init__to initialize some data attributes
self.x = x
self.y = y
c = Coordinate(3,4)
print(c)
print(c.x)
origin = Coordinate(0,0)
origin.y
Data attributes of an instance are called instance variables
Don’t provide argument for self, Python does this automatically
class Coordinate(object):
def __init__(self, x, y):
self.x= x
self.y= y
def distance(self, other):
x_diff_sq= (self.x-other.x)**2
y_diff_sq= (self.y-other.y)**2
return(x_diff_sq+y_diff_sq)**0.5
c = Coordinate(3,4)
origin = Coordinate(0,0)
c.distance(origin) # python by default realises c is an instance of a Coordinate
Coordinate.distance(c,origin) #another way of calling the same class
Python calls the str method when used with printon your class object
print(c) # prints some thing that dosen't make much sense
#adding __str__ method
class Coordinate(object):
def __init__(self, x, y):
self.x= x
self.y= y
def distance(self, other):
x_diff_sq= (self.x-other.x)**2
y_diff_sq= (self.y-other.y)**2
return(x_diff_sq+y_diff_sq)**0.5
def __str__(self): #special method
return"<" + str(self.x) + "," + str(self.y) + ">"
c = Coordinate(3,4)
print(c) #prints as defined in __str__ method
print(isinstance(c, Coordinate)) # to check if an object is a Coordinate
#Prints numbers in fraction format
class fraction(object):
def __init__(self, numer, denom):
self.numer= numer
self.denom= denom
def __str__(self):
return str(self.numer) + ' / ' + str(self.denom)
fraction(2,3)
f = fraction(2,3)
print(f)
# Adding getters , getNumer and getDenom
# To sepearate the ineternal representation from external , simply put not to mess with original value.
class fraction(object):
def __init__(self, numer, denom):
self.numer= numer
self.denom= denom
def __str__(self):
return str(self.numer) + ' / ' + str(self.denom)
def getNumer(self):
return self.numer
def getDenom(self):
return self.denom
f = fraction(2,3)
f.getNumer()
f.getDenom()
#Adding operations to fractions addition and subtraction.
class fraction(object):
def __init__(self, numer, denom):
self.numer= numer
self.denom= denom
def __str__(self):
return str(self.numer) + ' / ' + str(self.denom)
def getNumer(self): # Getter methods are better practice than just accessing an attribute directly
return self.numer
def getDenom(self):
return self.denom
def __add__(self, other): # regular addition is replaced by this method
numerNew = other.getDenom() * self.getNumer() \
+ other.getNumer() * self.getDenom()
denomNew= other.getDenom() * self.getDenom()
return fraction(numerNew, denomNew)
def __sub__(self, other):# regular substration is replaced by this method
numerNew = other.getDenom() * self.getNumer() \
-other.getNumer() * self.getDenom()
denomNew = other.getDenom() * self.getDenom()
return fraction(numerNew, denomNew)
def convert(self): #convert to decimal from fraction
return self.getNumer() / self.getDenom()
f = fraction(2,3)
g = fraction(4,5)
f.getNumer()
g.getDenom()
print(f + g)
print(f - g)
f.convert()
# a class to creat a list, check if member and remove from the list.
class intSet(object):
def __init__(self):
self.vals= []
def insert(self, e):
if not e in self.vals:
self.vals.append(e)
def member(self, e):
return e in self.vals
def remove(self, e):
try:
self.vals.remove(e)
except:
raise ValueError(str(e) + ' not found')
def __str__(self):
self.vals.sort()
result = ''
for e in self.vals:
result = result + str(e) + ','
return'{' + result[:-1] + '}'
a = intSet()
a.member(3)
print(a)
a.insert(3)
print(a)
a.insert(4)
a.insert(5)
a.insert(3)
print(a) #note 3 is not repeated
a.member(3)
a.member(5)
a.member(6)
print(a)
a.remove(3)
print(a)
a.remove(6) #Throws an error as expected
The official Python documentation says __repr__ is used to compute the “official” string representation of an object and __str__ is used to compute the “informal” string representation of an object
import datetime
today = datetime.datetime.now()
str(today) # calls __str__ , readable
repr(today) #calls __str__ , offical
eval('2018-08-05 06:52:41.933367') #str can not be reversed to datetime
eval('datetime.datetime(2018, 8, 5, 6, 52, 41, 933367)') #repr can , hence “official” string
Thus in a general every class you code must have a __repr__ and if you think it would be useful to have a string version of the object, as in the case of datetime create a __str__ function.
Bundle together objects that share
Use abstraction to make a distinction between how to implement an object vs how to use the object
Build layers of object abstractions that inherit behaviors from other classes of objects
Create our own classes of objects on top of Python’s basic classes
Class is the type
Class is defined generically
Class defines data and methods common across all instances
Instance is one particular object
Data values vary between instances
instance has the structure of the class
class Animal(object):
def __init__(self, age):
self.age= age
self.name= None
myanimal= Animal(3)
myanimal.age
myanimal.name
class Animal(object):
def __init__(self, age):
self.age= age
self.name= None
def get_age(self):
return self.age
def get_name(self):
return self.name
def set_age(self, newage):
self.age= newage
def set_name(self, newname=""): #gives "" as default output
self.name= newname
def __str__(self):
return "animal:"+str(self.name)+":"+str(self.age)
animal1 = Animal(10)
animal1.get_age()
animal1.get_name()
animal1.set_name("rocky")
animal1.get_name()
Author of class definition may change data attribute variable names
If you are accessing data attributes outside the class and class definition changes, may get errors
Outside of class, use getters and setters instead
#example age is stored as year
class Animal(object):
def __init__(self, age):
self.years= age
def get_age(self):
return self.years
Allows you to access data from outside class definition
Allows you to write to data from outside class definition
Allows you to create data attributes for an instance from outside class definition
It’s not good style to do any of these!
Parent class(superclass)
child class(subclass)
#regualr animal class as before
class Animal(object):
def __init__(self, age):
self.age= age
self.name= None
def get_age(self):
return self.age
def get_name(self):
return self.name
def set_age(self, newage):
self.age= newage
def set_name(self, newname=""): #gives "" as default output
self.name= newname
def __str__(self):
return "animal:"+str(self.name)+":"+str(self.age)
# Cat sub class
class Cat(Animal):
def speak(self):
print("meow")
def __str__(self):
return "cat:"+str(self.name)+":"+str(self.age)
add new functionality with speak()
__init__ is not missing, uses the Animal version
jelly = Cat(1)
jelly.set_name('JellyBelly')
print(jelly)
print(Animal.__str__(jelly)) #refrencing the parent class
# Another sub class
class Rabbit(Animal):
def speak(self):
print("meep")
def __str__(self):
return "rabbit: "+str(self.name)+":"+str(self.age)
jelly = Cat(1)
blob = Animal(1)
peter = Rabbit(5)
jelly.speak()
peter.speak()
blob.speak() #blob dosen't have attribute speak
#person subclass in Animal class
class Person(Animal):
def __init__(self, name, age):
Animal.__init__(self, age)
Animal.set_name(self, name)
self.friends= []
def get_friends(self):
return self.friends
def add_friend(self, fname):
if fname not in self.friends:
self.friends.append(fname)
def speak(self):
print("hello")
def age_diff(self, other):
# alternate way: diff = self.age-other.age
diff = self.get_age() -other.get_age()
if self.age> other.age:
print(self.name, "is", diff, "years older than", other.name)
else:
print(self.name, "is", -diff, "years younger than", other.name)
def __str__(self):
return "person:"+str(self.name)+":"+str(self.age)
eric= Person("Eric", 45)
john = Person("John", 55)
eric.speak()
eric.age_diff(john)
Person.age_diff(john,eric)
sub classes inherit all data attributes and methods of the parent class
tag used to give unique id to each new rabbit instance
class Rabbit(Animal):
tag = 1
def __init__(self, age, parent1=None, parent2=None):
Animal.__init__(self, age)
self.parent1 = parent1
self.parent2 = parent2
self.rid= Rabbit.tag
Rabbit.tag += 1
def get_rid(self):
return str(self.rid).zfill(3)
def get_parent1(self):
return self.parent1
def get_parent2(self):
return self.parent2
def __add__(self, other): #new add method
# returning object of same type as this class
return Rabbit(0, self, other)
def __eq__(self, other): # comparing ids of parents since ids are unique
#decide that two rabbits are equal if they have the same two parents
parents_same = self.parent1.rid == other.parent1.rid \
and self.parent2.rid == other.parent2.rid
parents_opposite = self.parent2.rid == other.parent1.rid \
and self.parent1.rid == other.parent2.rid
return parents_same or parents_opposite
peter = Rabbit(2)
peter.set_name('Peter')
hopsy= Rabbit(3)
hopsy.set_name('Hopsy')
cotton = Rabbit(1, peter, hopsy) # creats new instance with peter and hopsy as parents by calling the classs
cotton.set_name('Cottontail')
print(cotton)
print(cotton.get_parent1())
mopsy= peter + hopsy # creats new instance with peter and hopsy as parents by additon operation
mopsy.set_name('Mopsy')
print(mopsy.get_parent1())
print(mopsy.get_parent2())
print(mopsy == cotton)
Bundle together objects that share
Use abstraction to make a distinction between how to implement an object vs how to use the object
Build layers of object abstractions that inherit behaviors from other classes of objects
create our own classes of objects on top of Python’s basic classes
Reference